package spring.mybatis.mapper;

import com.me.study.javaCore.enumm.BaseEnum;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import spring.mybatis.domain.EnumValueHandler;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

public class MybatisTypeHandlerBeanPostProcessor implements BeanPostProcessor {
    private static final Logger LOGGER = LoggerFactory.getLogger(MybatisTypeHandlerBeanPostProcessor.class);

    private final String typeHandlersPackage;

    public MybatisTypeHandlerBeanPostProcessor(String typeHandlersPackage) {
        this.typeHandlersPackage = typeHandlersPackage;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof SqlSessionFactory) {
            SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) bean;
            try {
                TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();

                Set<Class<?>> classes = scanClasses(typeHandlersPackage);
                classes.forEach(cls -> {
                    typeHandlerRegistry.register(cls, EnumValueHandler.class);
                });
            } catch (Exception e) {
                // ignore or handle exception
            }
        }

        return bean;
    }

    /**
     * 扫描 BaseEnum 类型的 枚举类
     *
     * @param packagePatterns   需要扫描的 TypeHandler 目录
     * @return                  指定路径下所有实现了 BaseEnum接口 的 枚举类
     */
    private Set<Class<?>> scanClasses(String packagePatterns) {
        String[] packagePatternArray = StringUtils.tokenizeToStringArray(packagePatterns, ",; ");
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Set<Class<?>> classes = new HashSet<>();

        for (String packagePattern : packagePatternArray) {
            try {
                Resource[] resources = resolver.getResources("classpath*:" + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
                MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resolver);
                for (Resource resource : resources) {
                    ClassMetadata classMetadata = readerFactory.getMetadataReader(resource).getClassMetadata();
                    Class<?> clazz = Class.forName(classMetadata.getClassName());
                    if (!clazz.isInterface() && BaseEnum.class.isAssignableFrom(clazz)) {
                        if (clazz.isEnum()) {
                            classes.add(clazz);
                        }
                        else {
                            LOGGER.warn("该 BaseEnum 实现不是枚举类型: {}", clazz.getName());
                        }
                    }
                }
            } catch (IOException | ClassNotFoundException e) {
                LOGGER.error("类加载失败: ", e);
            }
        }

        return classes;
    }

}
